package com.dudy.service;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * author:zhaojunwei
 * dateTime:2018/3/14
 * email:zjw2023@163.com
 * 码云：https://gitee.com/dudy.zhao/zhugang_demo
 * 游戏中同步服务器时间事件
 **/
public class NIOService {
	/**
	 * 服务端的Channel通道类型
	 */
	private ServerSocketChannel server;
	/**
	 * 端口号
	 */
	private int port = 8080;
	/**
	 * 多路注册复用器 注册SelectKey.OP多种操作
	 */
	private Selector selector;
	/**
	 * 自定义接受和发送缓冲池
	 */
	private ByteBuffer recvBuffer = ByteBuffer.allocate(1024);
	private ByteBuffer sendBuffer = ByteBuffer.allocate(1024);
	/**
	 * 消息缓存队列
	 */
	private Map<SelectionKey, String> sessionMsg = new HashMap<SelectionKey, String>();
	/**
	 * 客户端编号缓存
	 */
	private Map<SelectionKey, Integer> clientMsg = new HashMap<SelectionKey, Integer>();
	/**
	 * 初始化客户端编号
	 */
	private static int client_no = 1592;

	public NIOService(Integer port) {
		this.port = port;
		try {
			//开启通道
			server = ServerSocketChannel.open();
			server.socket().bind(new InetSocketAddress(this.port));
			//将通道改成非阻塞
			server.configureBlocking(false);
			selector = Selector.open();
			server.register(selector, SelectionKey.OP_ACCEPT);
			System.out.println("NIO服务器已经初始化完毕，监听的端口是：" + this.port);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 服务器监听我们selector事件标签 驱动处理模块
	 */
	private void listener()  {
		try {
			while (true) {
                int select = selector.select();
                if (select == 0) {
                    continue;
                }
				Set<SelectionKey> selectionKeys = selector.selectedKeys();
				Iterator<SelectionKey> iterator = selectionKeys.iterator();
				while (iterator.hasNext()) {
					//拿到一个事件通道Channel，然后驱动处理模块处理标签
					process(iterator.next());
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 事件驱动
	 * @param selectionKey
	 */
	private void process(SelectionKey selectionKey) {
		SocketChannel client = null;
		//判断是否处理链接状态
		try {
			if (selectionKey.isValid() && selectionKey.isAcceptable()) {
				//接受客户端
				server.accept();
				++client_no;
				client.configureBlocking(false);
				//将客户端的下一步操作设置为读
				client.register(selector, SelectionKey.OP_READ);
			} else if (selectionKey.isValid() && selectionKey.isReadable()) {
				//清空读缓冲池
				recvBuffer.clear();
				client = (SocketChannel)selectionKey.channel();
				int len = client.read(recvBuffer);
				if (len > 0) {
					String msg = new String(recvBuffer.array(), 0, len);
					sessionMsg.put(selectionKey, msg);
					System.out.println("当前处理的线程是：" + Thread.currentThread().getId() + "，读取的客户端编号为：" + client_no + "，读取的信息为：" + msg);
					clientMsg.put(selectionKey, client_no);
					client.register(selector, SelectionKey.OP_WRITE);
				}
			} else if (selectionKey.isValid() && selectionKey.isWritable()) {
				if (!sessionMsg.containsKey(selectionKey)) {
					return;
				}
				client = (SocketChannel) selectionKey.channel();
				sendBuffer.clear();
				sendBuffer.put((sessionMsg.get(selectionKey) + "/n/r您好，您的请求已经完成").getBytes());
				//转换读写位
				sendBuffer.flip();
				client.write(sendBuffer);
				System.out.println("当前处理的线程是：" + Thread.currentThread().getId() + "，读取的客户端编号为：" + client_no);
				client.register(selector, SelectionKey.OP_READ);
			}
		} catch (IOException e) {
			e.printStackTrace();
			selectionKey.cancel();
			try {
				client.socket().close();
				client.close();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
			System.out.println("系统提示：客户端" + clientMsg.get(client_no) + "非法下线!");
			clientMsg.remove(client_no);
		}
	}

	public static void main(String[] args) {
		try {
			new NIOService(8080).listener();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
